package com.qire.manhua.view.comicReader.renderer;

import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.RectF;

/**
 * 图片资源实体，用于漫画阅读页章节图片管理
 */
public class PictureResource {

	//region    #图片资源基本属性 begin
	/** 图片资源所属映射的资源URI */
	public final String uri;

	/** 图片资源的唯一token，URI可能指向网路也可能指向本地，但token取的是URI中资源的唯一标识名 */
	public final String token;

	/** 用于资源映射获取真是bitmap的资源池，为了统一管理回收和复用资源并不持有图片，在需要的时候从池子内获取 */
	private IResourcePool resourcePool = ComicPicturePool.POOL;

	/** 资源的真是图片用于绘制，可能为空所以资源映射内部维护了一个自获取逻辑，确保在需要时可以获得图片资源，这个图片对象可能在某个时刻被释放 */
	private Bitmap drawBitmap = null;

	/** 视口宽度，用于将 drawBitmap 绘制成像时计算铺满屏幕的宽度及高度，如果未设置或值小于0则视口大小与图片大小一致 */
	private int viewportWidth = -1;

	/** 图片真实的宽高矩阵 */
	private Rect bitmapRect;

	/** 图片展示时的视口宽高 */
	private Rect viewportRect;

	/** 链表节点上的上一个图片资源 */
	protected PictureResource previousResource;

	/** 当前图片资源是否已读，用于统计章节内阅读进度 */
	private boolean isReaded = false;

	/** 当前图片资源的排序序号 */
	private int serialNumber = 1;

	//endregion #图片资源基本属性 end

	//region    #图片资源绘制相关属性 begin
	/** 图片资源绘制时所使用的画笔 */
	private Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);

	/** 图片下载进度百分比 */
	private float progress = 0;

	/** 下载进度绘制圆圈的半径 */
	private int progressRadius = 100;

	/** 下载进度绘制圆圈的起始角度 */
	private int startAngle = -90;

	/** 下载进度绘制圆圈的扇形角度 */
	private int sweepAngle = (int)(progress * 360);

	/** 下载进度绘制圆圈的前景色，高光进度颜色 */
	private int progressFg = Color.rgb(0x98,0x98,0x98);

	/** 下载进度绘制圆圈的背景色，阴暗的总进度颜色 */
	private int progressBg = Color.rgb(0x64,0x64,0x64);
	//endregion #图片资源绘制相关属性 end

	public PictureResource(String uri, int viewportWidth) {
		this.uri		= uri;
		this.token 		= token(uri);
		this.viewportWidth = viewportWidth;
		//设置字体大小
		paint.setTextSize(40);
		//设置线冒样式:圆
		paint.setStrokeCap(Paint.Cap.ROUND);
		//如果该项设置为true，则图像在动画进行中会滤掉对Bitmap图像的优化操作，加快显示速度，本设置项依赖于dither和xfermode的设置
		paint.setFilterBitmap(true);
		//设定是否使用图像抖动处理，会使绘制出来的图片颜色更加平滑和饱满，图像更加清晰
		paint.setDither(true);
	}

	/**
	 * 通过URI提取token的规则
	 * @param uri 目标URI
	 * @return token
	 */
	private final String token(String uri) {
		String tokenTmp = uri;
		tokenTmp = tokenTmp.substring(0,tokenTmp.lastIndexOf("."));
		tokenTmp = tokenTmp.substring(tokenTmp.lastIndexOf("/") + 1);
//		Pattern pattern = Pattern.compile("chapter/\\d+/\\d+/([a-z0-9]+)\\.(jpg|jpeg|gif|png)",Pattern.CASE_INSENSITIVE);
//		Matcher matcher = pattern.matcher(uri);
//		if(matcher.find()){
//			tokenTmp = matcher.group(1);
//		}
		return tokenTmp;
	}

	/**
	 * 释放回收资源，目前只是将绘制bitmap置空，因为真实的bitmap是由资源池提供释放管理的，所以不需要在这里真正回收，
	 * 但是对于资源池外的引用应该做到不用即时置空，以避免由于过多位置存在引用而导致释放不彻底OOM。
	 * 目前来说资源池的图片只提供给 {@link PictureResource} 使用，也应该尽可能避免图片资源流露到外部使用增加管理风险，
	 * 任何需要使用图片资源应该尽力委托给 {@link PictureResource} 来操作，减少 bitmap 暴漏的风险（即需要操作bitmap可以扩展这个类提供更丰富的方法）。
	 */
	public void recycled() {
		// resourcePool使用了弱类型保存了bitmap数据，这里不需要recycle，resourcePool会自己维护Bitmap的recycle;仅设置为null放弃强制引用即可
//		if(drawBitmap != null && !drawBitmap.isRecycled())
//			drawBitmap.recycle();
		drawBitmap = null;
	}

	/**
	 * 停止还未开始加载的图片加载
	 */
	public void stopLoad() {
		resourcePool.exitRequestEarly(this);
	}

	/**
	 * 当前资源映射的bitmap是可绘制的。
	 * @return
	 */
	public boolean canDrawn() {
		return drawBitmap != null && !drawBitmap.isRecycled();
	}

	/**
	 * 投一个图片请求到资源池中，如果资源池存在请求，则表示已经进入下载队列，否则添加投递请求。
	 * 发送图片请求应该是资源映射图片的唯一途径，这样避免多线程下的复杂问题。
	 * 而出发图片资源请求的行为也应该是由绘制函数或绘制重置按钮来触发，让问题变得简单，过去多入口触发下载应该被丢弃移除。
	 * @return true 表示已经进入下载队列， false 表示未进入下载队列。
	 */
	private boolean sendPictureRequest() {
		return resourcePool.sendPictureRequest(this, new IPictureLoader() {

			private int loadSize = 0;

			@Override
			public void loadBitmap(Bitmap bitmap) {
				PictureResource resource = PictureResource.this;
				resource.drawBitmap = bitmap;
			}

			@Override
			public void updateProgress(int stepSize, long totalSize) {
				loadSize += stepSize;
				PictureResource resource = PictureResource.this;
				resource.progress	= ((float) loadSize / (float) totalSize);
				resource.sweepAngle	= (int) (progress * 360);
			}

			@Override
			public void onStateChange(LoaderState state) {
				// todo: 需要处理加载异常
				// 如果连续下载完成 3 次 则标识这个图片资源可能出现问题了。需要提供一个处理方案来解决
				// 第一次找本地， 如果存在，返回图片，绘制时失败不可毁之
				// 发起第二次下载，先清除本地缓存，在请求，这个时候走网络下载。
				// 返回图片依旧存在问题，不可绘制，那么就可能是网络图片有问题，应该停止图片请求投递了。提供提示。
			}
		});
	}

	/**
	 * 绘制资源图片
	 * @param canvas 绘制的目标画布
	 * @return true 绘制成功， false 绘制失败
	 */
	public boolean draw(Canvas canvas) {
		/******************************************************************************
		 * 1.资源映射的真实图片是否可绘制
		 * 2.如果可绘制则绘制图片
		 * 3.如果不可绘制则查看是否正在下载
		 * 4.如果未进入下载中，则发送一个下载请求。
		 * 5.如果正在下载则绘制下载进度，
		 * 6.如果遇到其他异常情况，则显示重新下载按钮，进入手动触发下载（即发送一个下载请求到资源池）
		 ******************************************************************************/

		final Bitmap bitmap = drawBitmap;
		// 图片未加载为空或者图片加载过被释放都属于不可绘制，都应该重新加载图片，至于从新下载或是从缓冲区拿是资源池的职责。
		boolean canDrawn = bitmap != null && !bitmap.isRecycled();

		if(canDrawn) {
			// 可以绘制
			canvas.drawBitmap(bitmap, null, viewportRect, paint);
		} else {
			// 不可以绘制，判断是否进入下载状态
			if(sendPictureRequest()) {
				// 进入下载队列，更新下载进度状态图
				drawProgress(canvas);
			}
		}

		return canDrawn;
	}

	/**
	 * 绘制图片加载进度
	 * @param canvas
	 */
	private void drawProgress(Canvas canvas) {
		//设置是否为空心
		paint.setStyle(Paint.Style.STROKE);
		//设置画笔粗细
		paint.setStrokeWidth(10);

		paint.setColor(progressBg);
		canvas.drawCircle(viewportRect.centerX(), viewportRect.centerY(), progressRadius, paint);

		RectF arcRectF = new RectF();
		arcRectF.offsetTo(viewportRect.centerX(), viewportRect.centerY());
		arcRectF.inset(-progressRadius, -progressRadius);

		paint.setColor(progressFg);
		canvas.drawArc(arcRectF, startAngle, sweepAngle, false, paint);


		//设置是否为空心
		paint.setStyle(Paint.Style.FILL_AND_STROKE);
		//设置画笔粗细
		paint.setStrokeWidth(0);
		//文本规格容器
		Rect textRect = new Rect();

		String progressText = (int) (progress * 100) + "%";
		paint.getTextBounds(progressText, 0, progressText.length(), textRect);
		float textX = viewportRect.centerX() - textRect.width()  / 2;
		float textY = viewportRect.centerY() + textRect.height() / 4;

		canvas.drawText(progressText, textX, textY, paint);

		String serialNumberText = serialNumber + "";
		paint.getTextBounds(serialNumberText, 0, serialNumberText.length(), textRect);
		textX = viewportRect.centerX() - textRect.width() / 2;
		textY = viewportRect.centerY() - (progressRadius + (textRect.height() / 2));

		canvas.drawText(serialNumberText, textX, textY, paint);
	}

	/**
	 * 这张图片是否已阅读过
	 * @return false为未读，true为已读
	 */
	public boolean readedState() {
		return isReaded;
	}

	/**
	 * 读取这张图片，阅读到这章图片时用于记录读取到这张图片的状态。
	 * @return
	 */
	public PictureResource reading() {
		isReaded = true;
		return this;
	}

	/**
	 * 设置图片所在章节中的节点序号
	 * @param serialNumber 这个资源是章节中的第几张图片
	 */
	public void setSerialNumber(int serialNumber) {
		this.serialNumber = serialNumber;
	}

	/**
	 * @return 真实图片的大小矩阵
	 */
	public Rect getBitmapRect() {
		return bitmapRect;
	}

	/**
	 * @return 设置真实图片的矩阵大小
	 */
	public void setBitmapRect(Rect bitmapRect) {
		this.bitmapRect = bitmapRect;
	}

	/**
	 * 计算当前资源显示视口区域并返回,计算这个区域需要满足3个条件：
	 * 1.关联真实图片的大小需要已知，不能为空
	 * 2.上一个资源节点的视口区域可知来确定自己的起始点，如果因为上一个资源关联图片未加载不存在而无法计算则页不能确定当前视口
	 *   如果 beforePageResource 为 null，则表示当前节点为首个节点则不需要知道上一个视口大小。
	 * 3.限制宽度。
	 * @return null 表示无法计算出当前资源的视口大小，可能因为图片未加载或上一个节点存在但不能计算视口
	 */
	public Rect calcViewportRect() {
		// 已经计算过视口大小则直接返回视口大小。因为视口大小是等比例缩放的图片大小，相对固定，只有当显示View限制的宽度改变时视口大小才需要重新计算，
		// 所以如果有需求需要对 viewportWidth 大小修改应该在修改时制空 viewportRect = null 重新计算 viewportRect 的大小。
		if(viewportRect != null) {
			return viewportRect;
		}

		// 关联真实图片的大小需要已知，不能为空
		if(bitmapRect == null || bitmapRect.isEmpty()) {
			return null;
		}

		// 上一个资源节点是否存在，存在则计算，不存在则设置起始位置
        Rect previousRect = previousResource == null ? new Rect() : previousResource.calcViewportRect();

		// 上一个资源节点的视口无法获得计算结果， 这个过程式个递归过程，
		if(previousRect == null) {
			return null;
		}

		// 根据图片大小及视口限制宽度来计算视口大小，如果未设置 viewportWidth 大小则视口大小于图片大小一致。
		boolean notLimitWidthMode = viewportWidth <= 0;
		int limitHeight = (int) (bitmapRect.height() * ((float) viewportWidth / (float) bitmapRect.width()));
		int width = notLimitWidthMode ? bitmapRect.width() : viewportWidth;
		int height = notLimitWidthMode ? bitmapRect.height() : limitHeight;

		// 将视口大小移动至上一个节点的底部位置
		viewportRect = new Rect(0, 0, width, height);
        viewportRect.offsetTo(0, previousRect.bottom);

		return viewportRect;
	}

	/**
	 * 检查 x, y 是否包含于视口中, 如果视口无法计算则返回 false, 否则判定 x,y 是否存在于视口矩形内
	 * @param x x坐标
	 * @param y y坐标
	 * @return true 包含 false 不包含
	 */
	public boolean viewportContains(int x, int y) {
		return calcViewportRect() == null ? false : viewportRect.contains(x, y);
	}

	/**
	 * 上一个资源节点
	 * @return 当前资源的布局的上一个资源
	 */
	public PictureResource getPreviousResource() {
		return previousResource;
	}

	/**
	 * 设置上一个资源节点
	 * @param previousResource
	 */
	public void setPreviousResource(PictureResource previousResource) {
		this.previousResource = previousResource;
	}

	/**
	 * 当前节点是否为空
	 * @return
	 */
	public boolean isEmpty() {
		return uri == null || getBitmapRect() == null || calcViewportRect() == null;
	}

	@Override
	public boolean equals(Object obj) {
		
		if(obj == null)
			return false;
		
		if(obj instanceof PictureResource) {
			PictureResource res = (PictureResource) obj;
			return this.uri.equals(res.uri);
		}

		// 如果是下载任务则交给任务的 equals 去判定
		if(obj instanceof Runnable) {
			return obj.equals(this);
		}
		
		return super.equals(obj);
	}

	@Override
	public int hashCode() {
		return uri.hashCode();
	}
}
